package us.versus.them.bitly;

import flash.display.BitmapData;

class Rectangle {
	public var diff:Int;
	public var x1:Int;
	public var y1:Int;
	public var x2:Int;
	public var y2:Int;

	public function new( diff:Float, x1:Float , y1:Float , x2:Float , y2:Float ) {
		this.diff = Math.floor( diff );
		this.x1   = Math.floor( x1 );
		this.y1   = Math.floor( y1 );
		this.x2   = Math.floor( x2 );
		this.y2   = Math.floor( y2 );
	}
}

/**
 *
 * Ported from a cloud.php I wrote "Thu May 26 23:29:15 EDT 2005"
 * in the same vein...
 *
 * @author BrianHammond
 * @date Fri Feb  6 21:01:33 EST 2009
 *
 */
class Cloud implements Bitter {

	var stack : haxe.FastList< Rectangle >;
	var colorScheme:Int;

	public function new() {
	}

	public function reset( w:Int, h:Int, bitmapData:BitmapData, tmp:BitmapData ) : Void {
		colorScheme = Math.floor( Math.random() * 7 );

		stack = new haxe.FastList< Rectangle >();
		stack.add( new Rectangle( 128, 0, 0, w - 1, h -1 ) );
		//App.clear( w, h, bitmapData, 0 );
		initCloud( bitmapData, 128, w, h );
	}

	public function loop( w:Int, h:Int, bitmapData:BitmapData, tmp:BitmapData ) : Bool {
		var nu_stack = new haxe.FastList< Rectangle >();
		for ( rect in stack ) {
			cloud( bitmapData, rect, nu_stack );
		}
		stack = nu_stack;
		return false;
	}

	public function cloud( bitmapData, rect, nu_stack:haxe.FastList< Rectangle > ) {
		cloudy( 
			bitmapData
			, rect.diff
			, rect.x1
			, rect.y1
			, rect.x2
			, rect.y2
			, nu_stack
		);
	}

	function setGray( bitmap, x, y, g ) {
		bitmap.setPixel( 
			  Math.floor( x )
			, Math.floor( y )
			, switch( colorScheme ) {
				case 1: App.rgb( 64, g / 2, g );                           // blue
				case 2: App.rgb( g * 1, g * 2, g * 1.3 );                  // electric brocade
				case 3: App.rgb( g, g, g );                                // gray
				case 4: App.rgb( g * 2, g, g * 1 );                        // funny islands
				case 5: App.rgb( g, g, g * 2 );                            // weird ass brocade
				case 6: App.rgb( g  * 42 / 64 + 16, g * 16 / 64 + 16, g ); // purple
				default: App.rgb( 64, g / 2, g );                          // blue
			}
		);
	}

	function initCloud( bitmap, diff, w, h ) {
		setGray( bitmap,     0,     0, giveOrTake( 128, diff ) );
		setGray( bitmap, w - 1,     0, giveOrTake( 128, diff ) );
		setGray( bitmap, w - 1, h - 1, giveOrTake( 128, diff ) );
		setGray( bitmap,     0, h - 1, giveOrTake( 128, diff ) );
		return bitmap;
	}

	function cloudy( bitmap, diff, x1, y1, x2, y2, nu_stack:haxe.FastList< Rectangle > ) {
		var xmid = Math.floor( ( x2 - x1 ) / 2 ) + x1;
		var ymid = Math.floor( ( y2 - y1 ) / 2 ) + y1;

		/**
		  1 --a-- 2
		  | t   u |
		  d   e   b
		  | w   v |
		  4---c---3
		 */
		var color1 = bitmap.getPixel( x1, y1 ) & 0xFF;
		var color2 = bitmap.getPixel( x2, y1 ) & 0xFF;
		var color3 = bitmap.getPixel( x2, y2 ) & 0xFF;
		var color4 = bitmap.getPixel( x1, y2 ) & 0xFF;

		if ( diff < 2 ) diff = 2;
		var colorA = Math.floor( ( color2 + color1 ) / 2 );
		var colorB = Math.floor( ( color3 + color2 ) / 2 );
		var colorC = Math.floor( ( color4 + color3 ) / 2 );
		var colorD = Math.floor( ( color4 + color1 ) / 2 );
		var colorE = Math.floor( giveOrTake( ( color4 + color3 + color2 + color1 ) / 4, diff ) );

		// top middle 2: a
		setGray( bitmap, xmid,     y1, colorA );
		setGray( bitmap, xmid + 1, y1, colorA );

		// right middle 2: b
		setGray( bitmap, x2, ymid,     colorB );
		setGray( bitmap, x2, ymid + 1, colorB );

		// bottom middle 2: c
		setGray( bitmap, xmid,     y2, colorC );
		setGray( bitmap, xmid + 1, y2, colorC );

		// left middle 2: d
		setGray( bitmap, x1, ymid,     colorD );
		setGray( bitmap, x1, ymid + 1, colorD );

		// middle middle 4: e
		setGray( bitmap, xmid,     ymid,     colorE );
		setGray( bitmap, xmid + 1, ymid,     colorE );
		setGray( bitmap, xmid + 1, ymid + 1, colorE );
		setGray( bitmap, xmid,     ymid + 1, colorE );

		// with clouds whose w,h aren't 2^n, we get yucky gaps...
		setGray( bitmap, xmid - 1, ymid - 1, colorE );
		setGray( bitmap, xmid - 1, ymid - 0, colorE );
		setGray( bitmap, xmid - 1, ymid + 1, colorE );
		setGray( bitmap, xmid - 1, ymid + 2, colorE );

		/**
		  1 --a-- 2
		  | t   u |
		  d   e   b
		  | w   v |
		  4---c---3
		 */
		if ( xmid - x1 > 2 ) {
			var nu_diff = diff * 67 / 128;
			nu_stack.add( new Rectangle( nu_diff, x1,       y1,       xmid, ymid ) );
			nu_stack.add( new Rectangle( nu_diff, xmid + 1, y1,       x2,   ymid ) );
			nu_stack.add( new Rectangle( nu_diff, xmid + 1, ymid + 1, x2,   y2   ) );
			nu_stack.add( new Rectangle( nu_diff, x1,       ymid + 1, xmid, y2   ) );
		}

		return bitmap;
	}

	function giveOrTake( num:Float, err:Float ) : Int{
		var n = Math.floor( num - err + Math.random() * 2 * err );
		return if ( n < 0 ) {
			0;
		} else {
			if ( n > 255 ) {
				255;
			} else {
				n;
			}
		};
	}

}
